from django.shortcuts import render

# Create your views here.
from django.http.response import JsonResponse
# 1. 先导入对应的模型
from . import models

from django.views import View
import json
from django.http.response import HttpResponse


from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from django.db import IntegrityError


def parse_request_data(request):
    """根据Content-Type自动解析JSON或表单数据，返回统一字典格式"""
    content_type = request.content_type.lower()
    
    if content_type == 'application/json':
        print("content_type application/json")
        try:
            return json.loads(request.body)
        except json.JSONDecodeError:
            return {}  # 返回空字典或抛出异常
    else:
        print("content_type request.POST.dict")
        # 合并POST和FILES（适用于文件上传）
        return {**request.POST.dict(), **request.FILES}
    
from typing import Dict, Union, List
def convert_data_types(
    data: Dict[str, Union[str, int, float, bool]],
    int_fields: List[str] = None
) -> Union[Dict, JsonResponse]:
    """根据字段列表自动转换字典中的值为指定类型"""
    int_fields = int_fields or []


    # 类型转换逻辑
    try:
        # 转换整型字段
        for field in int_fields:
            if field in data:
                data[field] = int(data[field])  # 支持字符串数字或数值类型
        return data

    except (ValueError, TypeError) as e:
        # 捕获类型转换错误（如字符串无法转数字）
        return JsonResponse(
            {'error': f'Field "{field}" conversion failed: {str(e)}'},
            status=400
        )



# 使用 method_decorator 来装饰类中的所有相关方法，而不是每个方法单独装饰 免除对每个方法单独使用 @csrf_exempt
@method_decorator(csrf_exempt, name='dispatch')
class StudentView(View):
    def get(self, request):
        """获取多个学生数据"""
        # # 获取指定模型对应的数据表里面所有的数据记录
        """
        模型类名.objects.all()  # 获取模型对应的数据表的模型类对象
        """
        object_list = models.Student.objects.all()
        # print(type(object_list))
        """
        QuerySet是django的ORM中提供給我們使用的查询集对象【伪列表】，支持使用索引来限制查询结果的数量，但是不支持使用负数索引
        <class 'django.db.models.query.QuerySet'>
        """
        # 要获取单个模型对象
        # print(object_list[0], type(object_list[0])) # 赵华 <class 'student.models.Student'>
        # student = object_list[0]
        # # 获取模型对象的字段属性
        # print(student.id, student.pk)  # 获取主键
        # print(student.name, student.description) # 获取其他属性
        # print(student.created_time.strftime("%Y-%m-%d %H:%M:%S")) # 获取日期格式的内容
        # # 当字段声明中，使用choices可选值选项以后，在模型对象里面就可以通过get_<字段名>_display() 来获取当前选项的文本提示
        # print(student.status, student.get_status_display())

        # QuerySet里面的成员是模型对象，不能直接被json转换成数据，所以我们需要先转换对象为字典，然后经过json处理才可以给客户端
        student_list = []

        for object in object_list:
            student_list.append({
                "id": object.id,
                "name": object.name,
                "age": object.age,
                "sex": object.sex,
                "classmate": object.classmate,
                "mobile": object.mobile,
                "description": object.description,
                "created_time": object.created_time,
                "updated_time": object.updated_time,
                "status": object.get_status_display(),
            })


        # # 上面的代码也可以使用推导式一句话搞定，但是不好排查错误
        # student_list = [{
        #  "id": object.id,
        #  "name": object.name,
        #  "age": object.age,
        #  "sex": object.sex,
        #  "classmate": object.classmate,
        #   "description": object.description,
        #  } for object in object_list]
        """
        all()返回的是模型对象列表，如果要获取字典列表，则可以使用values()
        values() 调用时没有传递参数，则默认获取所有字段内容
        """
        # student_list = models.Student.objects.values("id", "name")
        # student_list = models.Student.objects.values()

        return JsonResponse(list(student_list), safe=False)

    def post(self,request):
        """添加数据"""
        
        # 1. 请求客户端发送过来的数据
        print(request.body)
        # 步骤1：通用数据解析
        raw_data = parse_request_data(request)
        print(raw_data)
        # 步骤2：动态提取所有字段（不写死字段名）
        data = {}
        for key in raw_data:
            value = raw_data.get(key)
            # 自动布尔值转换（支持'true'/'false'或'1'/'0'）
            if isinstance(value, str) and value.lower() in {'true', 'false', '1', '0'}:
                data[key] = value.lower() in {'true', '1'}
            else:
                data[key] = value
        
        # 步骤3：类型强校验（按需添加）
        # 示例：确保age为整数（非必须，根据业务需求）
 
        data = convert_data_types( data, int_fields=['age']  )

        print("Processed Data:", data)
        return JsonResponse({'status': 'success', 'data': data})
  

    def put(self,request):
        """更新数据"""
        data = {}
        return JsonResponse(data, status=201)

    def delete(self,request):
        """删除数据"""
        data = {}
        return JsonResponse(data, status=204)



@method_decorator(csrf_exempt, name='dispatch')
class StudentAPIView(View):
    """强化安全校验的学生信息接口"""

    ###################################################################
    # 公共工具方法
    ###################################################################
    """统一解析请求数据，支持JSON和表单"""
    def _parse_request_data(self, request):
        if request.content_type == 'application/json':
            try:
                return json.loads(request.body), None
            except json.JSONDecodeError:
                return None, 'Invalid JSON format 上传数据不符合要求'
        else:
            # 合并POST和GET参数（根据需求调整）
            return {**request.POST.dict(), **request.GET.dict()}, None

    """数据校验核心逻辑"""
    def _validate_student_data(self, data, is_create=True):
        """数据校验核心逻辑"""
        errors = {}
        
        # 必填字段校验（创建时必须存在，更新时可选）
        required_fields_create = ['name', 'age', 'classmate', 'mobile']
        if is_create:
            for field in required_fields_create:
                if field not in data or data[field] in (None, ''):
                    errors[field] = 'This field is required 上传数据不符合要求'
                    print(" errors : 411")
        # 字段类型校验
        field_rules = {
            'name': {
                'type': str, 
                'max_length': 15,
                'error': 'Name must be a string (max 15 characters)'
            },
            'age': {
                'type': int,
                'min': 0,
                'max': 150,
                'error': 'Age must be an integer between 0-150'
            },
            'sex': {
                'type': bool,
                'allow_null': False,
                'error': 'Sex must be a boolean (true/false)'
            },
            'classmate': {
                'type': str,
                'max_length': 50,
                'error': 'Classmate must be a string (max 50 characters)'
            },
            'mobile': {
                'type': str,
                'max_length': 20,
                'regex': r'^1[3-9]\d{9}$',  # 简单手机号格式校验
                'error': 'Invalid mobile number format'
            },
            'status': {
                'type': int,
                'choices': [0, 1, 2],
                'error': 'Status must be 0 (正常), 1 (未入学), or 2 (已毕业)'
            }
        }

        # 动态校验每个字段
        for field, rules in field_rules.items():
            if field not in data:
                continue  # 允许不传可选字段
            
            value = data[field]
            # 类型检查
            if not isinstance(value, rules['type']):
                # 尝试类型转换（如字符串转数字）
                try:
                    if rules['type'] == int:
                        data[field] = int(value)
                    elif rules['type'] == bool:
                        data[field] = str(value).lower() in ('true', '1')
                    else:
                        errors[field] = rules['error']
                except (ValueError, TypeError):
                    errors[field] = rules['error']


            # 额外规则检查
            if field == 'mobile' and 'regex' in rules:
                import re
                if not re.match(rules['regex'], str(value)):
                    errors[field] = rules['error']
                    print(" errors : 414")
            
            if field == 'status' and 'choices' in rules:
                if int(value) not in rules['choices']:
                    errors[field] = rules['error']
                    print(" errors : 415")

            # 长度检查
            if 'max_length' in rules and len(str(value)) > rules['max_length']:
                errors[field] = rules['error'] 

        return errors, data


    def ret(self, data):
        print(self.request.method) # 在类视图中，不仅可以通过视图方法中的参数，接收路由传递过来的请求对象，还可以通过self.request来或路由转发过来的请求对象
        return HttpResponse(data)

    # ************** 增删改查 **************

    # 查询 
    def student_query_api_01(self,data):
        
        # 检查是否存在具有相同name的学生
        existing_student = models.Student.objects.filter(name=data['name']).first()
        # 遍历数据 数据库存在的话
        if existing_student is not None:
            fields = existing_student._meta.get_fields()
            # 获取Student模型的_meta信息
            fields = existing_student._meta.get_fields()
            for field in fields:
                
                # 获取字段的值，注意对于DateTimeField等可能需要格式化输出
                value = getattr(existing_student, field.name)
                # print(f"字段名字 : {field.name}  _|_  value: {value} _|_ fieldmeta信息 : {field} ")
                print(f"{field.name.capitalize()}: {value}")
        else:
            print("数据不存在")
            return False
        return True
    # 查询 # 查询方式二  唯一性查询 遍历行查询 等
    def student_query_api_02(self,data):
        
        # 检查是否存在具有相同name的学生
        # first 查询一个结果, 查询不到，则返回None，查询多个，返回查询结果列表的第一个。
        # last  django还提供了last方法，可以获取结果列表中最后一个成员。
        # existing_student = models.Student.objects.filter(name=data['name']).first() 
        # existing_student = models.Student.objects.filter(name=data['name']).last() 
        existing_student = models.Student.objects.filter(name=data['name'])  
        print(f"existing_student {existing_student}")
        for i in existing_student:
            print(f"existing_student {i.name} {i.mobile}")

        # 查询方式二  唯一性查询
        try:
            existing_student = models.Student.objects.get(name=data['name'])
            print(f"existing_student {existing_student}")
        except models.Student.DoesNotExist:
            print("没有查询结果！")
        except models.Student.MultipleObjectsReturned:
            print("当前数据不是唯一的结果！")

        """all 获取所有数据"""
        student_list = models.Student.objects.all()
        print(student_list)       # 获取结果列表
        print(len(student_list))  # 获取结果列表的长度

        # 把结果列表中的所有模型对象转化成字典结构
        # student_list = models.Student.objects.all().values()
        student_list = models.Student.objects.filter(name=data['name']).values()
        # print(student_list)
        print(len(student_list), student_list[0]["name"])


        # 把结果列表中的所有模型对象转换成元组结构
        # student_list = models.Student.objects.all().values_list()
        student_list = models.Student.objects.filter(name=data['name']).values_list()
        print(len(student_list),student_list[0][0])

        student_list = models.Student.objects.filter(classmate=302).all()
        print(student_list)


        # count
        # 查询结果数量。实际上是一个聚合函数来的
        """count 获取结果总数"""
        # 没有任何条件的统计当前数据表的结果
        total = models.Student.objects.count()
        print(f"学生总人数：{total}")

        # 也可以添加where条件
        total = models.Student.objects.filter(classmate="301").count()
        print(f"学生301总人数：{total}")

        return True

    # 查询
    def student_query_api(self,data):
        
        # print("数据库查询 data : ",data)
        self.student_query_api_01(data)
        # self.student_query_api_02(data)
        
        # 如果没有则保存
        self.save_data(data)
        # 更新
        self.gengxin_data(data)
        # 删除数据
        self.shanchu_data(data)

        return
    # 保存数据 增加
    def save_data(self,cleaned_data):
        # pass
        existing_student = models.Student.objects.filter(mobile=cleaned_data['mobile']).first()
 
        if existing_student:
            print("该电话已存在")
            return
        
        # 学生不存在，创建并保存新数据
        student = models.Student(
            name=cleaned_data['name'],
            age=cleaned_data['age'],
            sex=cleaned_data['sex'],
            classmate=cleaned_data['classmate'],
            description=cleaned_data['description'],
            mobile=cleaned_data['mobile'],
            # 假设status字段在Student模型中已定义
            status=cleaned_data['status'],
        )
        student.save()
        print("学生信息已保存，ID为：", student.id)


        """使用create来完成添加一条数据"""
        # # 返回值就是添加后的模型对象，会有ID主键的 通过模型类.objects.create()保存。
        # student = models.Student.objects.create(
        #     name="赵本山",
        #     age=50,
        #     xingbie=True,
        #     classmate=301,
        #     description="一段小品"
        # )
        # print(student)
        # print(student.pk)  # print(student.id)

        # bulk_create
        # 通过模型类.objects.bulk_create()批量添加数据

        # """基于bulk_create添加多条数据"""
        # stu1 = Student(name="小黄人1号", age=17, xingbie=True, classmate=301, description="卜乃奈")
        # stu2 = Student(name="小黄人2号", age=17, xingbie=False, classmate=301, description="卜乃奈")
        # stu3 = Student(name="小黄人3号", age=17, xingbie=True, classmate=301, description="卜乃奈")
        # stu4 = Student(name="小黄人3号", age=17, xingbie=True, classmate=301, description="卜乃奈")
        # stu_list = [stu1,stu2,stu3,stu4]
        # ret = Student.objects.bulk_create(stu_list)
        # print(ret)
        # return HttpResponse("ok")

    # 更新数据
    def gengxin_data(self,cleaned_data):
        existing_student = models.Student.objects.filter(mobile=cleaned_data['mobile']).first()
 
        if existing_student:
            print("数据更新 : ",cleaned_data['name'])
            existing_student.name=cleaned_data['name']
            existing_student.age=cleaned_data['age']
            existing_student.sex=cleaned_data['sex']
            existing_student.classmate=cleaned_data['classmate']
            existing_student.description=cleaned_data['description']
            existing_student.mobile=cleaned_data['mobile']
            existing_student.status=cleaned_data['status'] 
            existing_student.save()
            return
        # update
        # 使用模型类.objects.filter().update()，基于update来完成更新满足条件的所有数据，结果是受影响的行数
        """update 更新多条数据"""
        # 基于update这种操作在数据库操作中，一般称之为"乐观锁"
        # update操作的执行效率比save要高！
        # update如果条件设置宽松，可以修改多条数据
        models.Student.objects.filter(name="刘德华").update(name="刘福荣")
        # SQL: update tb_student set name="刘福荣" where name="刘德华";
    # 删除数据
    def shanchu_data(self,cleaned_data):
        # 先查询要删除的数据
        student = models.Student.objects.filter(name=cleaned_data["name"]).first()
        if student:
            # 调用模型对象的delete方法进行删除
            student.delete()

        # 模型类.objects.filter().delete()
        """删除多条"""
        # 把符合条件的数据全部删除，注意：这种删除操作务必加上filter过滤条件，否则会变成全表删除
        # 返回值是删除的数量
        models.Student.objects.filter(name="小黄人").delete()


    # ************** 数据库进阶操作 **************



    def get(self,request):
        """只允许通过get请求访问,建议编写读取数据的页面,一般例如:首页,列表页,详情页"""
        # 视图中的视图方法里面的代码，与原来的函数视图中的代码，是一模一样的。原来怎么写，现在还是怎么写。
        return self.ret("hello, get")

    def post(self,request):
        """只允许通过post请求访问,一般用于上传,添加数据的页面"""

        """创建学生（带全字段校验）"""
        raw_data, parse_error = self._parse_request_data(request)
        if parse_error:
            return JsonResponse({'code': 410, 'error': parse_error}, status=410)

        # 执行校验
        errors, cleaned_data = self._validate_student_data(raw_data, is_create=True)
        if errors:
            return JsonResponse({'code': 411, 'errors': errors}, status=411)
        
        print("上传数据符合要求: ",cleaned_data,cleaned_data['name'])

        try:
            # 增删改查
            self.student_query_api(cleaned_data)
        except Exception as e:
            # 捕获其他可能的异常
            print(f"增删改查 有异常 {e}")
            return JsonResponse({'code': 412, 'errors': "增删改查 有异常 "}, status=412)

        return self.ret("hello, post")

    def put(self,request):
        """只允许通过put请求访问,一般用于修改,更新数据的页面"""
        return self.ret("hello, put")

    def patch(self,request):
        """只允许通过patch请求访问,一般用于修改,更新数据的页面"""
        return self.ret("hello, patch")

    def delete(self,request):
        """只允许通过delete请求访问,一般用于处理删除数据的页面"""
        return self.ret("hello, delete")



# 微信登录测试

# 使用 csrf_exempt 装饰器来跳过 CSRF 验证（仅用于开发测试，生产环境需正确配置 CSRF）
@csrf_exempt
def login(request):
    # print("request:",request)
    # return HttpResponse("<h1>你好，django</h1>")

    if request.method == 'GET':
        # 处理 GET 请求
        return HttpResponse("<h1>你好，django - GET 请求</h1>")
    elif request.method == 'POST':
        # 处理 POST 请求
        try:
            # 尝试解析 JSON 数据
            data = json.loads(request.body)
            print("POST 数据:", data)
 
            # 在这里添加你的登录逻辑，例如验证用户信息
            # ...
 
            # 返回 JSON 响应
            return JsonResponse({
                'success': True,
                'message': '登录成功',
                # 'userInfo': user_info,  # 可以在这里返回用户信息
            })
        except json.JSONDecodeError:
            # 如果解析 JSON 失败，返回错误信息
            return JsonResponse({
                'success': False,
                'message': '请求数据格式错误'
            }, status=400)
    else:
        # 处理其他类型的请求
        return HttpResponse("<h1>不支持的请求方法</h1>", status=405)
